;注意:若出现忙等无效或显示过快 请酌情调整代码中的延时!
;改进:
;1.基于数码管的键盘控制的多数制循环显示
; 规定如下:
;*用户按下非数字或者q键 提示无效键
;*用户按下q:退出
;*用户按下0: 循环显示十进制(0->9)
;*用户按下1: 循环显示十六进制(0->F) 
;*用户按下2~9: 循环显示对应的进制
;*例如:用户按下3 循环显示3进制(0->2)
;2.在显示过程中(实时检测按键) 用户按下任意键可返回菜单并清空数码管
;3.提供更友好的人机交互
DATA SEGMENT 
;-接口区-
IO8255C EQU 28AH              ;8255 PC口
IO8255D EQU 28BH              ;8255 控制端口 
;-数码表- 数码表对应详见文件尾部
LED DB 3FH,06H,5BH,4FH,66H,6DH,7DH,07H,7FH
    DB 67H,77H,7CH,39H,5EH,79H,71H
;-提示信息区-
MENU  DB 'Press 0,decimal system',0DH,0AH     
      DB 'Press 1,hexadecimal system',0DH,0AH  
      DB 'Press 2~9,other numerical system',0DH,0AH 
      DB 'Press q,quit',0DH,0AH,'$' ;菜单
WK   DB 'Wrong key!',0DH,0AH,'$'   ;无效按键
BM   DB 0DH,0AH,'Press any to come back Menu.',0DH,0AH,'$'
TC   DB 'Please turn off box power.',0DH,0AH,'$'   ;退出
DATA ENDS 
CODE SEGMENT 

ASSUME CS:CODE,DS:DATA 
START: 
;初始化设置
      ;数据段初始化
      MOV AX,DATA 
      MOV DS,AX 
      ;向8255控制端口写80H(1000 0000) 
      ;(1)特征位 (00)A方式0 (0)A输出 
      ;(0)C高4位输出 (0)B方式0 (0)B输出 (0)C低4位输出
      ;即ABC皆输出 工作方式为0
      MOV DX,IO8255D 
      MOV AL,80H 
      OUT DX,AL
      
      ;打印菜单
BG:   LEA DX,MENU
      MOV AH, 9 
      INT 21H 
      ;接收用户按键到AL中
      MOV AH,8
      INT 21H 
      ;输出按任意键返回的信息
      PUSH AX     ;保护AL
      LEA DX,BM 
      MOV AH,9
      INT 21H
      POP AX 
      ;根据用户按下的键 确定执行的分支
      CMP AL,'q'  
      JZ EXIT     ;按下q退出
      CMP AL,'9'
      JA WX       ;按键大于9 跳转无效输出
      CMP AL,'0'
      JB WX       ;按键小于0 跳转到无效输出
      CMP AL,'0'  
      JNZ NEXT1   ;按键不等0 继续判断
      MOV CX,10   ;按键等于0 设置CX为10 
      JMP SZ      ;跳到显示模块 SZ 
NEXT1:CMP AL,'1'        
      JNZ SETC   ;按键不等于1 跳转到2~9处理代码  
      MOV CX,16   ;按键等于1 设置CX为16
      JMP SZ      ;跳到显示模块 SZ
      
;SETC: 按键2-9的处理模块
SETC: MOV CX,0
      MOV CL,AL   ;将AL值给了CX 
      MOV DX,30H 
      SUB CX,DX   ;减去30H得到ASCII对应数字
      JMP SZ      ;跳到显示模块 SZ

;WX: 无效按键提示模块
WX:   LEA DX,WK 
      MOV AH,9
      INT 21H       ;打印信息
      JMP BG        ;跳回主菜单 重新等待输入

;SZ 循环设置
SZ:   LEA BX,LED     ;将数码表的入口偏移给BX
      PUSH CX        ;保存CX的值
;XS 循环显示
      ;将数码写入8255的C端 从而可以传给数码管
XS:   MOV AL,[BX]    ;DS:BX 对应的数码 给AL
      MOV DX,IO8255C 
      OUT DX,AL 
      CALL DELAY    ;延时 
      INC BX        ;BX指向下一个数码
      ;检测是否有用户输入 若有则返回菜单 不等待用户!
      MOV DL,0FFH
      MOV AH,6
      INT 21H     ;检测用户输入并放到AL
      JNZ RS      ;若AL不为0(表明有用户输入)则跳到RS
      ;循环判定
      LOOP XS 
      ;恢复CX的初值 并从头开始显示
      POP CX 
      JMP SZ      

;RS:重新开始 清空数码管并跳回主菜单
RS:   MOV AL,0
      MOV DX,IO8255C
      OUT DX,AL
      JMP BG
;延时函数 延时时间为 (CX)*(SI)个DEC SI指令时间 
DELAY: PUSH CX 
      MOV CX,0FFFFH 
FF:   MOV SI,05FFH 
ZJ:   DEC SI 
      JNZ ZJ
      LOOP FF 
      POP CX 
      RET 

;打印信息并退出 
EXIT: LEA DX,TC 
      MOV AH,9 
      INT 21H 
      MOV AH,4CH 
      INT 21H 

CODE ENDS 
END START 

;数码表说明      
;     h g f e d c b a  字符
; 3FH(0 0 1 1 1 1 1 1)  0
; 06H(0 0 0 0 0 1 1 0)  1 
; 5BH(0 1 0 1 1 0 1 1)  2
; 4FH(0 1 0 0 1 1 1 1)  3
; 66H(0 1 1 0 0 1 1 0)  4
; 6DH(0 1 1 0 1 1 0 1)  5
; 7DH(0 1 1 1 1 1 0 1)  6
; 07H(0 0 0 0 0 1 1 1)  7
; 7FH(0 1 1 1 1 1 1 1)  8
; 67H(0 1 1 0 0 1 1 1)  9
; 77H(0 1 1 1 0 1 1 1)  A
; 7CH(0 1 1 1 1 1 0 0)  b
; 39H(0 0 1 1 1 0 0 1)  C
; 5EH(0 1 0 1 1 1 1 0)  d
; 79H(0 1 1 1 1 0 0 1)  E
; 71H(0 1 1 1 0 0 0 1)  F

